/*
 * Copyright (c) 2009, Lombardi Software
 *  All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer in the documentation and/or other
 *   materials provided with the distribution.
 *
 * * Neither the name of Lombardi software nor the names of its
 *   contributors may be used to endorse or promote products
 *   derived from this software without specific prior
 *   written permission of Lombardi Software
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.lombardi.waves.helloworld.client;

import java.util.Map;
import java.util.HashMap;
import com.google.gwt.gadgets.client.Gadget;
import com.google.gwt.gadgets.client.UserPreferences;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ClickEvent;

/**
 * Basic example Google Wave Gadget created with GWT.
 *
 * @author amoffat Alex Moffat
 */
@Gadget.ModulePrefs(title = "Hello Wave World")
public class HelloWorld extends Gadget<UserPreferences> implements NeedsRpc {

    /**
     * The name of the single property in the state that we're interested in.
     */
    private static final String COUNT = "count";

    /**
     * The HelloWorld instance that is going to be used by the state callback.
     */
    private static HelloWorld instance;

    /**
     * Label showing the current count value.
     */
    private Label count;

    /**
     * The buttons to increment, decrement and reset the count.
     */
    private Button[] buttons;

    /**
     * Called to initialize the HelloWorld instance.
     *
     * @param userPreferences This is not used.
     */
    protected void init(UserPreferences userPreferences) {
        // Record this instance so that it can be used by stateUpdated.
        instance = this;

        // Build the label, buttons and the rest of the user interface.
        count = new Label("Count is 0");

        buttons = new Button[3];

        Button increment = new Button("Incr");
        increment.setTitle("Add one to the count.");
        increment.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {
                addToCurrentCountValue(1);
            }
        });
        buttons[0] = increment;

        Button decrement = new Button("Decr");
        decrement.setTitle("Subtract one from the count.");
        decrement.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {
                addToCurrentCountValue(-1);
            }
        });
        buttons[1] = decrement;

        Button reset = new Button("Reset");
        reset.setTitle("Set the count to zero.");
        reset.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {
                updateCurrentCount(0);
            }
        });
        buttons[2] = reset;

        FlowPanel buttonPanel = new FlowPanel();
        buttonPanel.setStyleName("buttonPanel");
        buttonPanel.add(increment);
        buttonPanel.add(decrement);
        buttonPanel.add(reset);

        SimplePanel clearDiv = new SimplePanel();
        clearDiv.setStyleName("clearFloats");

        FlowPanel fp = new FlowPanel();
        fp.setStyleName("gadget");
        fp.add(count);
        fp.add(buttonPanel);
        fp.add(clearDiv);

        RootPanel.get().add(fp);

        // Register to be notified when the state changes.
        registerStateChangeCallback();
    }

    /**
     * Add something to the current value for the count.
     *
     * @param amountToAdd The amount to add to the current value.
     */
    private void addToCurrentCountValue(int amountToAdd) {
        int newValue = getCurrentCount() + amountToAdd;
        updateCurrentCount(newValue);
    }

    /**
     * Retrieve the current value for the count, handling the case when that happens when the Wave is first created.
     *
     * @return The current value.
     */
    private int getCurrentCount() {
        String currentValue = WaveState.get(COUNT);
        int intValue = 0;
        // Null is returned when the wave is first created.
        if (currentValue != null) {
            intValue = Integer.parseInt(currentValue);
        }
        return intValue;
    }

    /**
     * Update the current value of the count to be the new value.
     *
     * @param newValue The nuew value.
     */
    private void updateCurrentCount(int newValue) {
        Map<String, String> newValues = new HashMap<String, String>();
        newValues.put(COUNT, Integer.toString(newValue));
        WaveState.submitDelta(newValues);
    }

    /**
     * Receive notification that the state has changed.
     */
    private static void stateUpdated() {
        instance.count.setText("Count is " + instance.getCurrentCount());
        // Not completely successful handling of playback. Need to prevent interaction
        // with state when in playback mode.
        if (waveIsPlayback()) {
            for (Button button : instance.buttons) {
                if (button.isEnabled()) {
                    button.setEnabled(false);
                }
            }
        } else {
            for (Button button : instance.buttons) {
                if (!button.isEnabled()) {
                    button.setEnabled(true);
                }
            }
        }
    }

    /**
     * True if in playback, false otherwise.
     *
     * @return True or False.
     */
    private static native boolean waveIsPlayback() /*-{
        return $wnd.wave.isPlayback();
    }-*/;

    /**
     * Register the stateUpdated method to be called when the state changes.
     */
    private native void registerStateChangeCallback() /*-{
        var wave = $wnd.wave;
//        if (wave && wave.isInWaveContainer()) { isInWaveContainer always returns false?
        if (wave) {
            wave.setStateCallback(@com.lombardi.waves.helloworld.client.HelloWorld::stateUpdated());
        }
    }-*/;

    /**
     * This method is required for the implementation of the NeedsRpc interface.
     *
     * @param feature This parameter is ignored.
     */
    public void dummyNeedsRpcMethod(RpcFeature feature) {
        // Nothing needed here.
    }
}
